﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Security.Cryptography;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;

namespace Chip8
{
    public partial class frmCheats : Form
    {
        public frmEmu MainForm;
        public List<Cheat> Cheats = new List<Cheat>();
        public clsCheats XMLCheats = new clsCheats();
        List<ResultsHolder> Results = new List<ResultsHolder>();
        public bool PerformedSearch = false;
        public class ResultsHolder
        {
            public string Address;
            public string Value;
            public ResultsHolder(string Address, string Value)
            {
                this.Address = Address;
                this.Value = Value;
            }
        }
        public class Cheat
        {
            public string ID;
            public string CheatName;
            public bool Enabled;
            public string Address;
            public string Value;
            public string OldValue;
            public Cheat(string ID, string Name, bool Checked, string Address, string Value, string OldValue)
            {
                this.ID = ID;
                this.CheatName = Name;
                this.Enabled = Checked;
                this.Address = Address;
                this.Value = Value;
                this.OldValue = OldValue;
            }
        }
        public frmCheats()
        {
            InitializeComponent();
        }

        private void frmCheats_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            this.Hide();
        }

        public void ResetCheats()
        {
            lstCheats.Items.Clear();
            foreach (Cheat ch in Cheats)
                MainForm.MainSystem.Memory[ushort.Parse(ch.Address, NumberStyles.HexNumber)] = 0;

            Cheats.Clear();
            foreach (Control c in gbcheats.Controls)
            {
                if (c is TextBox)
                    c.Text = "";
            }
            txtKnown.Text = "";
            lblResults.Text = "Results: 0";
            Results.Clear();
            lstresults.Items.Clear();
        }

        public void LoadCheats(string MD5)
        {
            if (!File.Exists("Cheats.cht"))
               XMLCheats.CreateXML();

            using (XmlTextReader textReader = new XmlTextReader("Cheats.cht")) //Open File
            {
                List<string> Params = new List<string>();
                bool MD5Match = false;
                while (textReader.Read()) //Read the file
                {
                    if (textReader.NodeType == XmlNodeType.Element)
                    {
                        if (textReader.Name == "CheatInfo")
                        {
                            Params.Clear();
                            if (textReader.GetAttribute(0) == MD5) {MD5Match = true; Params.Add(textReader.GetAttribute("ID"));} else MD5Match = false;        
                        }

                        if (textReader.Name == "CheatTitle" && MD5Match) Params.Add(textReader.ReadString());
                        if (textReader.Name == "Cheat" && MD5Match) Params.Add(textReader.ReadString());

                        if (Params.Count == 3)
                        {
                            Match m = Regex.Match(Params[2], @"([0-9A-F]{4})\?([0-9A-F]{2})\:([0-9A-F]{2})");
                            if (m.Success)
                            {
                                Cheats.Add(new Cheat(Params[0], Params[1], false, m.Groups[1].Value, m.Groups[3].Value, m.Groups[2].Value));
                                lstCheats.Items.Add(Params[1], false);
                            }
                        }
                    }
                }
            }
        }

        public bool IsHex(string C)
        {
            Match m = Regex.Match(C, "[A-F0-9]");
            if (m.Success)
                return false;

            return true;
        }

        public bool OnlyHexInString(string test)
        {
            return System.Text.RegularExpressions.Regex.IsMatch(test, @"\A\b[0-9a-fA-F]+\b\Z");
        }

        private void cmdAdd_Click(object sender, EventArgs e)
        {
            if (txtDescription.Text == "")
            {
                MessageBox.Show("Please enter a description", "Enter Description", MessageBoxButtons.OK);
                txtDescription.Focus();
                return;
            }

            if (txtAddress.Text == "" || !OnlyHexInString(txtAddress.Text))
            {
                MessageBox.Show("Please enter a valid 4 digit address.", "Invalid Address", MessageBoxButtons.OK);
                txtAddress.Focus();
                return;
            }

            if (ushort.Parse(txtAddress.Text, NumberStyles.HexNumber) > MainForm.MainSystem.Memory.Length - 1)
            {
                MessageBox.Show("Please enter a valid 4 digit address within the bounds of memory (0000 - " + (MainForm.MainSystem.Memory.Length - 1).ToString("X4") + ").", "Invalid Address", MessageBoxButtons.OK);
                txtAddress.Focus();
                return;
            }

            if (txtNew.Text == "" || !OnlyHexInString(txtNew.Text))
            {
                MessageBox.Show("Please enter a valid 2 digit new value.", "Invalid New Value", MessageBoxButtons.OK);
                txtOld.Focus();
                return;
            }

            if (txtOld.Text == "" || !OnlyHexInString(txtOld.Text))
            {
                MessageBox.Show("Please enter a valid 2 digit previous value.", "Invalid Old Value", MessageBoxButtons.OK);
                txtOld.Focus();
                return;
            }

            Cheats.Add(new Cheat(lstCheats.Items.Count.ToString(),txtDescription.Text, true, txtAddress.Text.PadLeft(4, '0'), txtNew.Text.PadLeft(2, '0'), txtOld.Text.PadLeft(2, '0')));
            lstCheats.Items.Add(txtDescription.Text, true);
            lstCheats.SelectedIndex = lstCheats.Items.Count - 1;
            XMLCheats.AddNewCheatXML(txtDescription.Text, txtAddress.Text.PadLeft(4, '0') + "?" + txtOld.Text.PadLeft(2, '0') + ":" + txtNew.Text.PadLeft(2, '0'), MainForm.MD5Hash,  (lstCheats.Items.Count - 1).ToString(), MainForm.RomName);

            foreach (Control c in this.Controls)
            {
                if (c is TextBox)
                    c.Text = "";
            }
        }

        private void txtAddress_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((Keys)e.KeyChar != Keys.Back && ModifierKeys != Keys.Control)
                e.Handled = IsHex(e.KeyChar.ToString().ToUpper());
        }

        private void tmrCheats_Tick(object sender, EventArgs e)
        {
            if (Cheats.Count > 0)
            {
                try
                {
                    foreach (Cheat ch in Cheats)
                    {
                        if (ch.Enabled)
                            MainForm.MainSystem.Memory[ushort.Parse(ch.Address, NumberStyles.HexNumber)] = byte.Parse(ch.Value, NumberStyles.HexNumber);

                        if (MainForm.MV.Visible)
                        {
                            if (MainForm.MV.hexMemory.ByteProvider.ReadByte(ushort.Parse(ch.Address, NumberStyles.HexNumber)) != MainForm.MainSystem.Memory[ushort.Parse(ch.Address, NumberStyles.HexNumber)])
                            {
                                MainForm.MV.hexMemory.ByteProvider.WriteByte(ushort.Parse(ch.Address, NumberStyles.HexNumber), MainForm.MainSystem.Memory[ushort.Parse(ch.Address, NumberStyles.HexNumber)]);
                                MainForm.MV.hexMemory.Invalidate();
                            }
                        }
                    }
                }
                catch { };
            }

            for (int x = 0; x < lstresults.Items.Count; x++)
            {
                string[] Line = Regex.Split(lstresults.Items[x].ToString(), " ");
                if (MainForm.MainSystem.Memory[ushort.Parse(Line[0], NumberStyles.HexNumber)] != int.Parse(Line[1], NumberStyles.HexNumber))
                    lstresults.Items[x] = Line[0] + " " + MainForm.MainSystem.Memory[ushort.Parse(Line[0], NumberStyles.HexNumber)].ToString("X2");
            }
        }

        private void lstCheats_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (lstCheats.SelectedIndex >= 0)
            {
                if (txtDescription.Text == Cheats[lstCheats.SelectedIndex].CheatName)
                {
                    if (e.NewValue == CheckState.Checked)
                        Cheats[lstCheats.SelectedIndex].Enabled = true;
                    else
                    {
                        Cheats[lstCheats.SelectedIndex].Enabled = false;
                        MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)] = byte.Parse(Cheats[lstCheats.SelectedIndex].OldValue, NumberStyles.HexNumber);

                        if (MainForm.MV.Visible)
                        {
                            if (MainForm.MV.hexMemory.ByteProvider.ReadByte(ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)) != MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)])
                            {
                                MainForm.MV.hexMemory.ByteProvider.WriteByte(ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber), MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)]);
                                MainForm.MV.hexMemory.Invalidate();
                            }
                        }
                    }
                }
            }
        }

        private void lstCheats_Click(object sender, EventArgs e)
        {
            if (lstCheats.SelectedIndex >= 0)
            {
                txtDescription.Text = Cheats[lstCheats.SelectedIndex].CheatName;
                txtAddress.Text = Cheats[lstCheats.SelectedIndex].Address;
                txtNew.Text = Cheats[lstCheats.SelectedIndex].Value;
                txtOld.Text = Cheats[lstCheats.SelectedIndex].OldValue;
            }
        }

        private void cmdUpdate_Click(object sender, EventArgs e)
        {
            if (lstCheats.SelectedIndex >= 0)
            {
                if (txtDescription.Text == "")
                {
                    MessageBox.Show("Please enter a description", "Enter Description", MessageBoxButtons.OK);
                    txtDescription.Focus();
                    return;
                }

                if (txtAddress.Text == "" || !OnlyHexInString(txtAddress.Text))
                {
                    MessageBox.Show("Please enter a valid 4 digit address.", "Invalid Address", MessageBoxButtons.OK);
                    txtAddress.Focus();
                    return;
                }

                if (ushort.Parse(txtAddress.Text, NumberStyles.HexNumber) > MainForm.MainSystem.Memory.Length - 1)
                {
                    MessageBox.Show("Please enter a valid 4 digit address within the bounds of memory (0000 - " + (MainForm.MainSystem.Memory.Length - 1).ToString("X4") + ").", "Invalid Address", MessageBoxButtons.OK);
                    txtAddress.Focus();
                    return;
                }

                if (txtNew.Text == ""  || !OnlyHexInString(txtNew.Text))
                {
                    MessageBox.Show("Please enter a valid 2 digit new value.", "Invalid New Value", MessageBoxButtons.OK);
                    txtOld.Focus();
                    return;
                }

                if (txtOld.Text == "" || !OnlyHexInString(txtOld.Text))
                {
                    MessageBox.Show("Please enter a valid 2 digit previous value.", "Invalid Old Value", MessageBoxButtons.OK);
                    txtOld.Focus();
                    return;
                }
                XMLCheats.UpdateCheatXML(MainForm.MD5Hash, Cheats[lstCheats.SelectedIndex].ID, txtDescription.Text, txtAddress.Text.PadLeft(4, '0') + "?" + txtOld.Text.PadLeft(2, '0') + ":" + txtNew.Text.PadLeft(2, '0'));
                Cheats[lstCheats.SelectedIndex].CheatName = txtDescription.Text;
                Cheats[lstCheats.SelectedIndex].Address = txtAddress.Text.PadLeft(4, '0');
                Cheats[lstCheats.SelectedIndex].Value = txtNew.Text.PadLeft(2, '0');
                Cheats[lstCheats.SelectedIndex].OldValue = txtOld.Text.PadLeft(2, '0');

                lstCheats.Items[lstCheats.SelectedIndex] = txtDescription.Text;
            }
        }

        private void cmdDelete_Click(object sender, EventArgs e)
        {
            if (lstCheats.SelectedIndex >= 0)
            {
                MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)] = byte.Parse(Cheats[lstCheats.SelectedIndex].OldValue, NumberStyles.HexNumber);

                if (MainForm.MV.Visible)
                {
                    if (MainForm.MV.hexMemory.ByteProvider.ReadByte(ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)) != MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)])
                    {
                        MainForm.MV.hexMemory.ByteProvider.WriteByte(ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber), MainForm.MainSystem.Memory[ushort.Parse(Cheats[lstCheats.SelectedIndex].Address, NumberStyles.HexNumber)]);
                        MainForm.MV.hexMemory.Invalidate();
                    }
                }

                XMLCheats.DeleteCheatXML(MainForm.MD5Hash, Cheats[lstCheats.SelectedIndex].ID);
                Cheats.RemoveAt(lstCheats.SelectedIndex);
                lstCheats.Items.RemoveAt(lstCheats.SelectedIndex);

                foreach (Control c in gbcheats.Controls)
                {
                    if (c is TextBox)
                        c.Text = "";
                }
            }
        }

        public string CreateMD5(string File)
        {
            MD5 md5 = MD5.Create();
            StringBuilder sb = new StringBuilder();

            using (FileStream fs = new FileStream(@File, FileMode.Open))
            {
                foreach (byte b in md5.ComputeHash(fs))
                    sb.Append(b.ToString("x2").ToUpper());
            }
            return sb.ToString();
        }

        private void cmdKnown_Click(object sender, EventArgs e)
        {
            if (txtKnown.Text.Trim() != "" && OnlyHexInString(txtKnown.Text))
            {
                lstresults.Items.Clear();
                List<string> MarkRemove = new List<string>();
                if (Results.Count > 0 && PerformedSearch)
                {
                    foreach (ResultsHolder rh in Results)
                    {
                        if (MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2") != txtKnown.Text)
                            MarkRemove.Add(rh.Address);
                        else
                            rh.Value = MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2");
                    }

                    foreach (string Address in MarkRemove)
                    {
                        foreach (ResultsHolder rh in Results)
                        {
                            if (rh.Address == Address)
                            {
                                Results.Remove(rh);
                                break;
                            }
                        }
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                    {
                        lstresults.Items.Add(rh.Address + " " + rh.Value);
                    }
                }
                else
                {
                    int Address = 0;
                    foreach (byte addr in MainForm.MainSystem.Memory)
                    {
                        if (addr.ToString("X2") == txtKnown.Text)
                            Results.Add(new ResultsHolder(Address.ToString("X4"), txtKnown.Text));
                        Address++;
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                        lstresults.Items.Add(rh.Address + " " + rh.Value);

                    PerformedSearch = true;
                }
            }
            else
                MessageBox.Show("Invalid value entered.  Please only enter a hexidecimal number.", "Invalid Value", MessageBoxButtons.OK);
        }

        private void cmdReset_Click(object sender, EventArgs e)
        {
            Results.Clear();
            PerformedSearch = true;
            int Address = 0;
            foreach (byte addr in MainForm.MainSystem.Memory)
            {
                Results.Add(new ResultsHolder(Address.ToString("X4"), addr.ToString("X2")));
                Address++;
            }
            lstresults.Items.Clear();
            txtKnown.Text = "";

            lblResults.Text = "Initialized: " + (Results.Count - 1).ToString();
        }

        private void cmdEqual_Click(object sender, EventArgs e)
        {
            if (PerformedSearch)
            {
                lstresults.Items.Clear();
                List<string> MarkRemove = new List<string>();
                if (Results.Count > 0)
                {
                    foreach (ResultsHolder rh in Results)
                    {
                        if (MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2") != rh.Value)
                            MarkRemove.Add(rh.Address);
                    }

                    foreach (string Address in MarkRemove)
                    {
                        foreach (ResultsHolder rh in Results)
                        {
                            if (rh.Address == Address)
                            {
                                Results.Remove(rh);
                                break;
                            }
                        }
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        PerformedSearch = false;
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                        lstresults.Items.Add(rh.Address + " " + rh.Value);
                }
            }
            else
            {
                MessageBox.Show("Please initialize search first using 'Reset' or 'Known Value'.", "Initialize Memory", MessageBoxButtons.OK);
                PerformedSearch = false;
                return;
            }
        }

        private void cmdGreater_Click(object sender, EventArgs e)
        {
            if (PerformedSearch)
            {
                lstresults.Items.Clear();
                List<string> MarkRemove = new List<string>();
                if (Results.Count > 0)
                {
                    foreach (ResultsHolder rh in Results)
                    {
                        if (MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)] > int.Parse(rh.Value, NumberStyles.HexNumber))
                            rh.Value = MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2");
                        else
                            MarkRemove.Add(rh.Address);
                    }

                    foreach (string Address in MarkRemove)
                    {
                        foreach (ResultsHolder rh in Results)
                        {
                            if (rh.Address == Address)
                            {
                                Results.Remove(rh);
                                break;
                            }
                        }
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        PerformedSearch = false;
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                        lstresults.Items.Add(rh.Address + " " + rh.Value);
                }
            }
            else
            {
                MessageBox.Show("Please initialize search first using 'Reset' or 'Known Value'.", "Initialize Memory", MessageBoxButtons.OK);
                PerformedSearch = false;
                return;
            }
        }

        private void cmdLessThan_Click(object sender, EventArgs e)
        {
            if (PerformedSearch)
            {
                lstresults.Items.Clear();
                List<string> MarkRemove = new List<string>();
                if (Results.Count > 0)
                {
                    foreach (ResultsHolder rh in Results)
                    {
                        if (MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)] < int.Parse(rh.Value, NumberStyles.HexNumber))
                            rh.Value = MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2");
                        else
                            MarkRemove.Add(rh.Address);
                    }

                    foreach (string Address in MarkRemove)
                    {
                        foreach (ResultsHolder rh in Results)
                        {
                            if (rh.Address == Address)
                            {
                                Results.Remove(rh);
                                break;
                            }
                        }
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        PerformedSearch = false;
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                        lstresults.Items.Add(rh.Address + " " + rh.Value);
                }
            }
            else
            {
                MessageBox.Show("Please initialize search first using 'Reset' or 'Known Value'.", "Initialize Memory", MessageBoxButtons.OK);
                PerformedSearch = false;
                return;
            }
        }

        private void cmdNotEqual_Click(object sender, EventArgs e)
        {
            if (PerformedSearch)
            {
                lstresults.Items.Clear();
                List<string> MarkRemove = new List<string>();
                if (Results.Count > 0)
                {
                    foreach (ResultsHolder rh in Results)
                    {
                        if (MainForm.MainSystem.Memory[ushort.Parse(rh.Address, NumberStyles.HexNumber)].ToString("X2") == rh.Value)
                            MarkRemove.Add(rh.Address);
                    }

                    foreach (string Address in MarkRemove)
                    {
                        foreach (ResultsHolder rh in Results)
                        {
                            if (rh.Address == Address)
                            {
                                Results.Remove(rh);
                                break;
                            }
                        }
                    }

                    lblResults.Text = " Results: " + Results.Count.ToString();

                    if (Results.Count == 0)
                    {
                        MessageBox.Show("No Results Found.", "No Results", MessageBoxButtons.OK);
                        PerformedSearch = false;
                        return;
                    }

                    foreach (ResultsHolder rh in Results)
                        lstresults.Items.Add(rh.Address + " " + rh.Value);
                }
            }
            else
            {
                MessageBox.Show("Please initialize search first using 'Reset' or 'Known Value'.", "Initialize Memory", MessageBoxButtons.OK);
                PerformedSearch = false;
                return;
            }
        }

        private void lstresults_DoubleClick(object sender, EventArgs e)
        {
            if (lstresults.Items.Count > 0)
            {
                string[] Line = Regex.Split(lstresults.SelectedItem.ToString(), " ");
                txtDescription.Text = Line[0];
                txtAddress.Text = Line[0];
                txtOld.Text = Line[1];
                txtNew.Text = Line[1];
                txtNew.Focus();
            }
        }



    }
}
